<!DOCTYPE html>
<html lang="zh-CH" >
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="author" content="" />
	
	
	
	<title>Unity3D-UI框架 ｜ zansimple</title>
	
    
    
    <meta name="description" content="博主的小游戏开发已经正式开展了起来了，游戏的定位主要就是乱斗游戏，首先就是通过写文章来记录一下自己游戏开发中遇到的问题，难点什么的。废话不多说直接进主题。
UI框架 游戏的开始当然是要先做界面了，研究了几天弄了个 UI 框架，主要功能就是由 UIManager 来控制界面的显隐，层级，界面导航的功能，
BaseUI 基类 我们先定义一个基类，所有需要通过 UIManager 控制的面板都必须继承这个基类。这个基类里面主要功能就是此界面的显隐方法，当前的层级，当前界面显示的上个界面，界面的类型等等。
//UI界面的基类public class BaseUI : MonoBehaviour {public UIType uiType = new UIType();protected EUiId uiId = EUiId.NullUI;protected EUiId beforeuiId = EUiId.NullUI;private int currentDepth = 1;public int CurrentDepth{get{return currentDepth;}set{currentDepth = value;}}public EUiId GetUiId{get{return uiId;}}public EUiId GetBeforeuiId{get{return beforeuiId;}}public bool IsNeedUpdateStack{get{if (uiType." />
    

    

	
    
    <link rel="shortcut icon" href="https://lenzan.github.io/images/favicon.ico" />

    <link rel="stylesheet" type="text/css" media="screen" href="https://lenzan.github.io/css/normalize.css" />
    <link rel="stylesheet" type="text/css" media="screen" href="https://cdn.jsdelivr.net/npm/animate.css@4.1.0/animate.min.css" />
    <link rel="stylesheet" type="text/css" media="screen" href="https://lenzan.github.io/css/zozo.css" />
	<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css" />
    <link rel="stylesheet" type="text/css" media="screen" href="https://lenzan.github.io/css/highlight.css" />

    
    
</head>

<body>
    <div class="main animate__animated animate__fadeInDown">
        <div class="nav_container animated fadeInDown">
    <div class="site_nav" id="site_nav">
        <ul>
            
            <li>
                <a href="/">首页</a>
            </li>
            
            <li>
                <a href="/">unity</a>
            </li>
            
            <li>
                <a href="/">归档</a>
            </li>
            
            <li>
                <a href="/">关于</a>
            </li>
            
        </ul>
    </div>
    <div class="menu_icon">
        <a id="menu_icon"><i class="ri-menu-line"></i></a>
    </div>
</div>
        <div class="header animated fadeInDown">
    <div class="site_title_container">
        <div class="site_title">
            <h1>
                <a href="https://lenzan.github.io/">
                    <span>zansimple</span>
                    <img src="https://lenzan.github.io/images/logo.svg" />
                </a>
            </h1>
        </div>
        <div class="description">
            <p class="sub_title">专注于Unity虚拟现实开发，游戏开发</p>
            <div class="my_socials">
                
                <a href="https://lenzan.github.io/index.xml" type="application/rss+xml" title="rss" target="_blank"><i
                        class="ri-rss-fill"></i></a>
            </div>
        </div>
    </div>
</div>
        <div class="content">
            <div class="post_page">
                <div class="post animate__animated animate__fadeInDown">
                    <div class="post_title post_detail_title">
                        <h2><a href='/posts/Unity/UIManager/'>Unity3D-UI框架</a></h2>
                        <span class="date">2018.08.15</span>
                    </div>
                    <div class="post_content markdown"><p>博主的小游戏开发已经正式开展了起来了，游戏的定位主要就是乱斗游戏，首先就是通过写文章来记录一下自己游戏开发中遇到的问题，难点什么的。废话不多说直接进主题。</p>
<h3 id="ui框架">UI框架</h3>
<p>游戏的开始当然是要先做界面了，研究了几天弄了个 UI 框架，主要功能就是由 UIManager 来控制界面的显隐，层级，界面导航的功能，</p>
<h3 id="baseui-基类">BaseUI 基类</h3>
<p>我们先定义一个基类，所有需要通过 UIManager 控制的面板都必须继承这个基类。这个基类里面主要功能就是此界面的显隐方法，当前的层级，当前界面显示的上个界面，界面的类型等等。</p>
<pre><code>//UI界面的基类
public class BaseUI : MonoBehaviour {
    public UIType uiType = new UIType();
    protected EUiId uiId = EUiId.NullUI;
    protected EUiId beforeuiId = EUiId.NullUI;
    private int currentDepth = 1;

    public int CurrentDepth
    {
        get
        {
            return currentDepth;
        }

        set
        {
            currentDepth = value;
        }
    }
    public EUiId GetUiId
    {
        get
        {
            return uiId;
        }
    }
    public EUiId GetBeforeuiId
    {
        get
        {
            return beforeuiId;
        }
    }

    public bool IsNeedUpdateStack
    {
        get
        {
            if (uiType.rootType == EUIRootType.KeepAbove)
            {
                return false;
            }
            if (uiType.showMode == EShowUIMode.NoReturn)
            {
                return false;
            }
            return true;
        }

    }
    protected Transform thisTrans;
    protected virtual void Awake()
    {
        thisTrans = this.transform;
        InitUIOnAwake();
    }

    public virtual void InitUIOnAwake()
    {

    }
    public virtual void InitUIData()
    {
        if (uiType == null)
        {
            uiType = new UIType();
        }
    }

    public virtual void ShowUI()
    {
        this.gameObject.SetActive(true);
    }

    public virtual void HideUI(DelAfterHideUI del)
    {
        this.gameObject.SetActive(false);
        if (del != null)
        {
            del();
        }
    }

    public virtual void DestroyUI()
    {
        BeforeDestroy();
        Destroy(this.gameObject);
    }

    protected virtual void BeforeDestroy()
    {

    }
}
</code></pre>
<h3 id="类型预制的映射">类型/预制的映射</h3>
<p>主要是记录一下，需要加载面板的路径。</p>
<pre><code>//UI类型对应的本地保存文件的名字
    public static Dictionary&lt;EUiId, string&gt; dicName = new Dictionary&lt;EUiId, string&gt;()
    {
        { EUiId.LoginUI,&quot;UIPanel/LoginUI&quot; },
        { EUiId.MainUI,&quot;UIPanel/MainUI&quot;},
        ...
    };
</code></pre>
<h3 id="模式">模式</h3>
<pre><code>public enum EShowUIMode//显示UI的模式
{
    DoNothing,
    HideOther,//显示自己，隐藏其他的
    NoReturn//不需要反向切换
}
</code></pre>
<h3 id="ui-类型">UI 类型</h3>
<pre><code>public enum EUiId//UI类型
{
    NullUI,
    LoginUI,
    MainUI,
    ...
}
</code></pre>
<h3 id="界面的节点">界面的节点</h3>
<pre><code>public enum EUIRootType//UI父节点
{
    KeepAbove,//保持在最上方
    Normal,//普通UI
}
</code></pre>
<h3 id="ui-类型-1">UI 类型</h3>
<pre><code>public class UIType
{
    public bool isResetReturnUIInfor = false;
    public EUIRootType rootType = EUIRootType.Normal;
    public EShowUIMode showMode = EShowUIMode.DoNothing;
}
</code></pre>
<h3 id="uimanager">UIManager</h3>
<p>UI 控制UI面板的逻辑的东西都在这里面</p>
<pre><code>using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIReturnInfor
{
    public BaseUI willBeShowUI;
    public List&lt;EUiId&gt; listReturn;
}
public class UIManager : MonoBehaviour
{
    public static UIManager instance = null;//单例
    private Dictionary&lt;EUiId, BaseUI&gt; dicAllUI;//保存所有的UI

    private Dictionary&lt;EUiId, BaseUI&gt; dicShowUI;//保存正在显示的UI

    private Stack&lt;UIReturnInfor&gt; stackReturnInfor;

    private BaseUI currentUI;//当前显示的UI
    private BaseUI beforeUI;//上一个显示的UI
    public RectTransform uiRootTrans;//UI父节点
    public RectTransform keepAboveUIRoot;//保持在上面的UI的父节点
    public RectTransform normalUIRoot;//常规显示的UI的父节点

    //private int keepAboveUIRootDepth = 300;
    //private int normalUIRootDepth = 100;

    void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        if (dicAllUI == null)
        {
            dicAllUI = new Dictionary&lt;EUiId, BaseUI&gt;();
        }
        if (dicShowUI == null)
        {
            dicShowUI = new Dictionary&lt;EUiId, BaseUI&gt;();
        }
        if (stackReturnInfor == null)
        {
            stackReturnInfor = new Stack&lt;UIReturnInfor&gt;();
        }
        InitUIManager();
    }

    //初始化
    public void InitUIManager()
    {
        if (dicAllUI != null)
        {
            dicAllUI.Clear();
        }
        if (dicShowUI != null)
        {
            dicShowUI.Clear();
        }
        DontDestroyOnLoad(uiRootTrans);
        //ShowUI(EUiId.LoginUI);
    }
    //根据UI类型显示UI
    public void ShowUI(EUiId uiId)
    {
        BaseUI baseUI = JudgeShowUI(uiId);//判断Uiid是否已经显示出来了
        if (baseUI != null)
        {
            baseUI.ShowUI();
            dicShowUI[uiId] = baseUI;
            if (baseUI.uiType.isResetReturnUIInfor)
            {
                ClearStackReturnInfor();
            }
        }
        else
        {
            return;
        }
        if (baseUI.uiType.rootType == EUIRootType.Normal)
        {
            beforeUI = currentUI;
            currentUI = baseUI;
        }

    }

    public BaseUI JudgeShowUI(EUiId uiId)
    {
        //判断uiid 是不是已经显示出来了
        if (dicShowUI.ContainsKey(uiId))
        {
            return null;
        }
        BaseUI baseUI = GetBaseUI(uiId);
        //如果baseUI在dicAllUI里面没有找到说明这个窗体还没有被加载过
        if (baseUI == null)
        {
            //加载
            if (GameDefine.dicName.ContainsKey(uiId))
            {
                GameObject theUI = CorResMgr.Get().LoadResOnly(GameDefine.dicName[uiId]) as GameObject;
                theUI.name = uiId.ToString();
                if (theUI != null)
                {
                    GameObject willShowUI = Instantiate(theUI);
                    willShowUI.SetActive(true);

                    //把窗体放到对应的节点下面
                    baseUI = willShowUI.GetComponent&lt;BaseUI&gt;();
                    RectTransform theRoot = GetTheRoot(baseUI);
                    GameTool.AddChildToParent(theRoot, willShowUI.GetComponent&lt;RectTransform&gt;());
                    theUI = null;
                    dicAllUI[uiId] = baseUI;
                }
                else
                {
                    Debug.Log(&quot;UIManager &gt;&gt; &quot; + &quot;you want to load UI no found...&quot;);
                }
            }
        }
        UpdateStack(baseUI);
        return baseUI;

    }

    public void ClearStackReturnInfor()
    {
        if (stackReturnInfor != null)
        {
            stackReturnInfor.Clear();
        }
    }

    private void UpdateStack(BaseUI baseUI)
    {
        if (baseUI.IsNeedUpdateStack)
        {
            //将要移除的窗体ID列表
            List&lt;EUiId&gt; removeKey = null;
            //存放需要添加进栈的窗体列表
            List&lt;BaseUI&gt; maxToMinUI = new List&lt;BaseUI&gt;();
            //存放根据depth值 从大到小排序好的窗体列表
            List&lt;EUiId&gt; newList = new List&lt;EUiId&gt;();
            UIReturnInfor uiReturnInfor = new UIReturnInfor();

            if (dicShowUI.Count &gt; 0)
            {
                foreach (KeyValuePair&lt;EUiId, BaseUI&gt; item in dicShowUI)
                {
                    if (item.Value.uiType.rootType != EUIRootType.KeepAbove)
                    {
                        item.Value.HideUI(null);
                        if (removeKey == null)
                        {
                            removeKey = new List&lt;EUiId&gt;();
                        }
                        removeKey.Add(item.Key);
                    }
                    if (item.Value.IsNeedUpdateStack)
                    {
                        maxToMinUI.Add(item.Value);
                    }
                }
                if (removeKey != null)
                {
                    for (int i = 0; i &lt; removeKey.Count; i++)
                    {
                        dicShowUI.Remove(removeKey[i]);
                    }
                }

                if (maxToMinUI.Count &gt; 0)
                {
                    maxToMinUI.Sort(delegate (BaseUI a, BaseUI b) { return a.CurrentDepth.CompareTo(b); });
                    for (int i = 0; i &lt; maxToMinUI.Count; i++)
                    {
                        newList.Add(maxToMinUI[i].GetUiId);
                    }
                    uiReturnInfor.willBeShowUI = baseUI;
                    uiReturnInfor.listReturn = newList;
                    stackReturnInfor.Push(uiReturnInfor);
                }
            }
        }

        if (baseUI.uiType.showMode == EShowUIMode.NoReturn)
        {
            HideAllUI(true);
        }
    }

    private void HideAllUI(bool isHideAboveUI)
    {
        List&lt;EUiId&gt; listRemove = null;
        if (isHideAboveUI)
        {
            foreach (KeyValuePair&lt;EUiId, BaseUI&gt; item in dicShowUI)
            {
                item.Value.HideUI(null);
            }
            dicShowUI.Clear();
        }
        else
        {
            foreach (KeyValuePair&lt;EUiId, BaseUI&gt; item in dicShowUI)
            {
                if (item.Value.uiType.rootType == EUIRootType.KeepAbove)
                {
                    continue;
                }
                else
                {
                    if (listRemove == null)
                    {
                        listRemove = new List&lt;EUiId&gt;();
                        listRemove.Add(item.Key);
                        item.Value.HideUI(null);
                    }
                }
            }
        }
        if (listRemove != null)
        {
            for (int i = 0; i &lt; listRemove.Count; i++)
            {
                listRemove.Remove(listRemove[i]);
            }
        }
    }

    private RectTransform GetTheRoot(BaseUI baseUI)
    {
        if (baseUI.uiType.rootType == EUIRootType.Normal)
        {
            return normalUIRoot;
        }
        if (baseUI.uiType.rootType == EUIRootType.KeepAbove)
        {
            return keepAboveUIRoot;
        }
        else
        {
            return uiRootTrans;
        }
    }

    public BaseUI GetBaseUI(EUiId uiId)
    {
        if (dicAllUI.ContainsKey(uiId))
        {
            return dicAllUI[uiId];
        }
        else
        {
            return null;
        }
    }
    public void HideUI(EUiId uiId, DelAfterHideUI del)
    {
        HideTheUI(uiId, del);
    }
    public void HideTheUI(EUiId uiId, DelAfterHideUI del)
    {
        if (!dicShowUI.ContainsKey(uiId))
        {
            return;
        }
        if (del != null)
        {
            del += delegate { dicShowUI.Remove(uiId); };
            dicShowUI[uiId].HideUI(del);
        }
        else
        {
            dicShowUI[uiId].HideUI(null);
            dicShowUI.Remove(uiId);
        }
    }

    public void ClickReturn()
    {
        if (stackReturnInfor.Count == 0)
        {
            if (currentUI == null)
            {
                return;
            }
            EUiId beforeUiId = currentUI.GetBeforeuiId;
            if (beforeUiId != EUiId.NullUI)
            {
                HideUI(currentUI.GetUiId, delegate { ShowUI(beforeUiId); });
            }
        }
        else
        {
            UIReturnInfor uiReturnInfor = stackReturnInfor.Peek();
            if (uiReturnInfor != null)
            {
                EUiId theId = uiReturnInfor.willBeShowUI.GetUiId;
                if (dicShowUI.ContainsKey(theId))
                {
                    HideUI(theId, delegate
                    {
                        if (!dicShowUI.ContainsKey(uiReturnInfor.listReturn[0]))
                        {
                            BaseUI baseUI = GetBaseUI(uiReturnInfor.listReturn[0]);
                            baseUI.ShowUI();
                            dicShowUI[baseUI.GetUiId] = baseUI;

                            this.beforeUI = currentUI;
                            this.currentUI = baseUI;
                            stackReturnInfor.Pop();
                        }
                    });
                }
            }
        }
    }
}
</code></pre>
<h3 id="loginui">LoginUI</h3>
<pre><code>public class LoginUI : BaseUI {

    private InputField userNameInputField;//用户名输入框
    private InputField passWordInputField;//密码输入框
    private Button startButton;//开始游戏按钮
    public override void InitUIOnAwake()
    {
        userNameInputField = GameTool.FindTheChild(this.gameObject, &quot;Field Username/InputField&quot;).GetComponent&lt;InputField&gt;();
        passWordInputField = GameTool.FindTheChild(this.gameObject, &quot;Field Password/InputField&quot;).GetComponent&lt;InputField&gt;();
        startButton = GameTool.FindTheChild(this.gameObject, &quot;Log In Button&quot;).GetComponent&lt;Button&gt;();

        startButton.onClick.AddListener(delegate () { OnStartClick(); });
        InitUIData();
    }

    public override void InitUIData()
    {
        base.InitUIData();
        this.uiId = EUiId.LoginUI;
        this.uiType.rootType = EUIRootType.Normal;
        this.uiType.isResetReturnUIInfor = true;
    }

    private void OnStartClick()
    {
        UIManager.instance.ShowUI(EUiId.MainUI);
    }

}
</code></pre>
<h3 id="总结">总结</h3>
<p>目前 UI 框架就做到这里了，大部分功能都可以实现。下一步要做的就是把主要的 UI 面板做出来可以实现界面之间的跳转。</p>
</div>
                    <div class="post_footer">
                        
                        <div class="meta">
                            <div class="info">
                                <span class="field tags">
                                    <i class="ri-stack-line"></i>
                                    
                                    <a href="https://lenzan.github.io/tags/Unity3D/">Unity3D</a>
                                    
                                </span>
                            </div>
                        </div>
                        
                    </div>
                </div>
                
                
            </div>
        </div>
    </div>
    <a id="back_to_top" href="#" class="back_to_top"><span>△</span></a>
    <footer class="footer">
    <div class="powered_by">
        <a href="https://varkai.com">Designed by VarKai,</a>
        <a href="http://www.gohugo.io/">Proudly published with Hugo</a>
    </div>

    <div class="footer_slogan">
        <span></span>
    </div>
</footer>
    <script src="https://lenzan.github.io/js/jquery-3.5.1.min.js"></script>
<link href="https://lenzan.github.io/css/fancybox.min.css" rel="stylesheet">
<script src="https://lenzan.github.io/js/fancybox.min.js"></script>
<script src="https://lenzan.github.io/js/zozo.js"></script>






</body>

</html>